#ifndef COMPILER_H
#define COMPILER_H

#include <vector>
#include <map>

#include <wx/string.h>
#include <wx/filename.h>
#include <wx/dynarray.h>
#include <wx/regex.h>
#include "compileoptionsbase.h"
#include "compileroptions.h"

class CompilerCommandGenerator;
class M_Code_Project;
class ProjectBuildTarget;
class ProjectFile;
class wxXmlNode;

enum CompilerLineType {
  cltNormal = 0,
  cltWarning,
  cltError,
  cltInfo
};

struct RegExStruct {
    RegExStruct()
      : desc( _T( "Unknown" ) ), lt( cltError ), filename( 0 ), line( 0 ), regex( _T( "" ) ), regexCompiled( false ) {
      memset( msg, 0, sizeof( msg ) );
    }
    RegExStruct( const RegExStruct& rhs )
      : desc( rhs.desc ), lt( rhs.lt ), filename( rhs.filename ), line( rhs.line ), regex( rhs.regex ), regexCompiled( false ) {
      memcpy( msg, rhs.msg, sizeof( msg ) );
    }
    RegExStruct( const wxString&  _desc, CompilerLineType _lt, const wxString&  _regex, int _msg, int _filename = 0,
                 int _line = 0, int _msg2 = 0, int _msg3 = 0 )
      : desc( _desc ), lt( _lt ), filename( _filename ), line( _line ), regex( _regex ), regexCompiled( false ) {
      msg[0] = _msg;
      msg[1] = _msg2;
      msg[2] = _msg3;
    }
    RegExStruct& operator=( const RegExStruct &obj ) {
      desc = obj.desc;
      lt = obj.lt;
      regex = obj.regex;
      regexCompiled = false;
      filename = obj.filename;
      line = obj.line;
      memcpy( msg, obj.msg, sizeof( msg ) );
      return *this;
    }

    bool operator!=( const RegExStruct& other ) {
      return !( *this == other );
    }
    bool operator==( const RegExStruct& other ) {
      return ( desc == other.desc
               && lt == other.lt
               && regex == other.regex
               && msg[0] == other.msg[0]
               && msg[1] == other.msg[1]
               && msg[2] == other.msg[2]
               && filename == other.filename
               && line == other.line );
    }

    wxString GetRegExString() const { return regex; }
    void SetRegExString( const wxString &str ) {
      if( regex != str ) {
        regex = str;
        regexCompiled = false;
      }
    }
    bool HasRegEx() const { return !regex.empty(); }
    const wxRegEx& GetRegEx() const { CompileRegEx(); return regexObject; }

    wxString desc;
    CompilerLineType lt;
    int msg[3];
    int filename;
    int line;

  private:
    void CompileRegEx() const {
      if( !regex.empty() && !regexCompiled ) {
        regexObject.Compile( regex );
        regexCompiled = true;
      }
    }
    wxString regex;
    mutable wxRegEx regexObject;
    mutable bool regexCompiled;
};
typedef std::vector<RegExStruct> RegExArray;

enum CommandType {
  ctCompileObjectCmd = 0,
  ctGenDependenciesCmd,
  ctCompileResourceCmd,
  ctLinkExeCmd,
  ctLinkConsoleExeCmd,
  ctLinkDynamicCmd,
  ctLinkStaticCmd,
  ctLinkNativeCmd,
  ctCount
};

enum CompilerLoggingType {
  clogFull,
  clogSimple,
  clogNone
};

enum AutoDetectResult {
  adrDetected,
  adrGuessed
};

struct CompilerPrograms {
  wxString C;
  wxString CPP;
  wxString LD;
  wxString LIB;
  wxString MAKE;
  wxString DBGconfig;
};

struct CompilerSwitches {
  static const CompilerLoggingType defaultLogging = clogFull;
  wxString includeDirs;
  wxString libDirs;
  wxString linkLibs;
  wxString defines;
  wxString genericSwitch;
  wxString objectExtension;
  bool forceFwdSlashes;
  bool forceLinkerUseQuotes;
  bool forceCompilerUseQuotes;
  bool needDependencies;
  CompilerLoggingType logging;
  wxString libPrefix;
  wxString libExtension;
  bool linkerNeedsLibPrefix;
  bool linkerNeedsLibExtension;
  bool linkerNeedsPathResolved;
  bool supportsPCH;
  wxString PCHExtension;
  bool UseFlatObjects;
  bool UseFullSourcePaths;
  wxChar includeDirSeparator;
  wxChar libDirSeparator;
  wxChar objectSeparator;
  int statusSuccess;
  CompilerSwitches();
};


struct CompilerTool {
  CompilerTool( const wxString& command_in = wxEmptyString, const wxString& extensions_in = wxEmptyString, const wxString& generatedFiles_in = wxEmptyString )
    : command( command_in ), extensions( GetArrayFromString( extensions_in ) ), generatedFiles( GetArrayFromString( generatedFiles_in ) ) {}
  CompilerTool( const CompilerTool& rhs )
    : command( rhs.command ), extensions( rhs.extensions ), generatedFiles( rhs.generatedFiles ) {}
  bool operator==( const CompilerTool& rhs ) const { return command == rhs.command && extensions == rhs.extensions && generatedFiles == rhs.generatedFiles; }
  bool operator!=( const CompilerTool& rhs ) const { return !( *this == rhs ); }

  wxString      command;
  wxArrayString extensions;
  wxArrayString generatedFiles;
};

typedef std::vector<CompilerTool> CompilerToolsVector;

class Compiler : public CompileOptionsBase {
  public:
    static const wxString FilePathWithSpaces;
    Compiler( const wxString& name, const wxString& ID, const wxString& parentID = wxEmptyString, int weight = 50 );
    virtual ~Compiler();
    virtual bool IsValid();
    virtual CompilerLineType CheckForWarningsAndErrors( const wxString& line );
    virtual wxString GetLastErrorFilename() { return m_ErrorFilename; }
    virtual wxString GetLastErrorLine() { return m_ErrorLine; }
    virtual wxString GetLastError() { return m_Error; }
    virtual const wxString& GetName() const { return m_Name; }
    virtual const wxString& GetMasterPath() const { return m_MasterPath; }
    virtual const wxArrayString& GetExtraPaths() const { return m_ExtraPaths; }
    virtual const CompilerPrograms& GetPrograms() const { return m_Programs; }
    virtual const CompilerSwitches& GetSwitches() const { return m_Switches; }
    virtual const CompilerOptions& GetOptions() const { return m_Options; }
    virtual const wxString& GetCommand( CommandType ct, const wxString& fileExtension = wxEmptyString ) const;
    virtual const CompilerTool* GetCompilerTool( CommandType ct, const wxString& fileExtension = wxEmptyString ) const;
    virtual CompilerToolsVector& GetCommandToolsVector( CommandType ct ) { return m_Commands[ct]; }
    virtual const RegExArray& GetRegExArray() { return m_RegExes; }
    virtual void LoadDefaultRegExArray( bool globalPrecedence = false );
    virtual void SetName( const wxString& name ) { m_Name = name; }
    virtual void SetMasterPath( const wxString& path ) { m_MasterPath = path; m_NeedValidityCheck = true; }
    virtual void SetExtraPaths( const wxArrayString& paths ) { m_ExtraPaths = paths; m_NeedValidityCheck = true; }
    virtual void SetPrograms( const CompilerPrograms& programs ) { m_Programs = programs; m_NeedValidityCheck = true; }
    virtual void SetSwitches( const CompilerSwitches& switches ) { m_Switches = switches; }
    virtual void SetOptions( const CompilerOptions& options ) { m_Options = options; }
    virtual void SetRegExArray( const RegExArray& regexes ) { m_RegExes = regexes; }
    virtual void SaveSettings( const wxString& baseKey );
    virtual void LoadSettings( const wxString& baseKey );
    virtual void Reset();
    virtual void ReloadOptions();
    virtual AutoDetectResult AutoDetectInstallationDir() = 0;
    const wxString& GetID() const { return m_ID; }
    const wxString& GetParentID() const { return m_ParentID; }
    static wxString CommandTypeDescriptions[ctCount];
    const wxString GetVersionString() const { return m_VersionString; };
    virtual CompilerCommandGenerator* GetCommandGenerator( M_Code_Project *project );
    void SetCOnlyFlags( const wxString& flags ) { m_SortOptions[0] = flags; };
    void SetCPPOnlyFlags( const wxString& flags ) { m_SortOptions[1] = flags; };
    const wxString& GetCOnlyFlags()   { return m_SortOptions[0]; };
    const wxString& GetCPPOnlyFlags() { return m_SortOptions[1]; };
    bool WithMultiLineMsg() { return m_MultiLineMessages; };
    wxString MakeInvalidCompilerMessages() const;
  protected:
    friend class CompilerFactory;
    Compiler( const Compiler& other );
    virtual Compiler* CreateCopy() = 0;
    bool IsUniqueID( const wxString& ID ) { return m_CompilerIDs.Index( ID ) == wxNOT_FOUND; }
    void MakeValidID();
    void LoadDefaultOptions( const wxString& name, int recursion = 0 );
    void LoadRegExArray( const wxString& name, bool globalPrecedence = false, int recursion = 0 );
    bool EvalXMLCondition( const wxXmlNode* node );
    wxString GetExecName( const wxString& name );
    void MirrorCurrentSettings();
    wxString m_Name;
    wxString m_MasterPath;
    wxArrayString m_ExtraPaths;
    CompilerToolsVector m_Commands[ctCount];
    CompilerPrograms m_Programs;
    CompilerSwitches m_Switches;
    CompilerOptions m_Options;
    RegExArray m_RegExes;
    wxString m_ErrorFilename;
    wxString m_ErrorLine;
    wxString m_Error;
    wxString m_VersionString;
    wxString m_SortOptions[2];
    int m_Weight;
    bool m_MultiLineMessages;
  private:
    wxString m_ID;
    wxString m_ParentID;
    static wxArrayString m_CompilerIDs;
    bool m_Valid;
    bool m_NeedValidityCheck;

    struct MirrorSettings {
      wxString Name;
      wxString MasterPath;
      wxArrayString ExtraPaths;
      CompilerPrograms Programs;
      wxArrayString CompilerOptions_;
      wxArrayString ResourceCompilerOptions;
      wxArrayString LinkerOptions;
      wxArrayString IncludeDirs;
      wxArrayString ResIncludeDirs;
      wxArrayString LibDirs;
      wxArrayString LinkLibs;
      wxArrayString CmdsBefore;
      wxArrayString CmdsAfter;
      CompilerToolsVector Commands[ctCount];
      CompilerSwitches Switches;
      CompilerOptions Options;
      RegExArray RegExes;
      wxString SortOptions[2];
    };
    MirrorSettings m_Mirror;
    bool m_Mirrored;
};

#endif
